package com.CmJava.servlet;

import com.CmJava.annotation.Controller;
import com.CmJava.annotation.RequestMapping;
import com.CmJava.servlet.executionChain.HandlerExecutionChain;
import com.CmJava.servlet.executionChain.handlerMethodMapping.AbstractHandlerMethodMapping;
import com.CmJava.servlet.executionChain.handlerMethodMapping.HandlerMethod;
import com.CmJava.servlet.executionChain.handlerMethodMapping.MethodParameter;
import com.CmJava.servlet.handlerAdapter.HandlerAdapter;
import com.CmJava.servlet.handlerAdapter.RequestMappingHandlerAdapter;
import com.CmJava.servlet.handlerMapping.HandlerMapping;
import com.CmJava.servlet.handlerMapping.RequestMappingHandlerMapping;
import com.CmJava.servlet.modelAndView.ModelAndView;
import com.CmJava.servlet.modelAndView.view.InternalResourceView;
import com.CmJava.servlet.modelAndView.view.View;
import com.CmJava.servlet.requestToViewNameTranslator.DefaultRequestToViewNameTranslator;
import com.CmJava.servlet.requestToViewNameTranslator.RequestToViewNameTranslator;
import com.CmJava.servlet.viewResolver.ContentNegotiatingViewResolver;
import com.CmJava.servlet.viewResolver.ViewResolver;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.File;
import java.io.IOException;
import java.lang.reflect.Method;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;

public class DispatcherServlet extends FrameworkServlet {

    //请求路径和Controller的Method映射
    private List<HandlerMapping> handlerMappings;
    //执行对应请求方法的执行器
    private List<HandlerAdapter> handlerAdapters;
    //请求中是否带文件
    private boolean multipartRequestParsed=false;
    //因为没有Spring的Context，这里直接把Method的Mapping设置静态属性，好传参
    public static AbstractHandlerMethodMapping abstractHandlerMethodMapping = new AbstractHandlerMethodMapping();
    //从请求路径中获取视图名
    private RequestToViewNameTranslator viewNameTranslator;
    //视图解析器，用来处理返回哪个html等视图
    private List<ViewResolver> viewResolvers;

    @Override
    public void init() throws ServletException {
        super.init();
        initServletBean();
    }


    private void initServletBean() {
        initStrategies();
    }

    public void doService(HttpServletRequest req, HttpServletResponse resp) {
        doDispatch(req, resp);
    }

    /**
     * 分发请求至对应的controller
     *
     * @param request
     * @param response
     */
    private void doDispatch(HttpServletRequest request, HttpServletResponse response) {
        HttpServletRequest processedRequest = request;
        HandlerExecutionChain mappedHandler = null;

        ModelAndView mv = null;


        //判断是否携带文件
        multipartRequestParsed = this.checkMultipart(request);
        //获取对应请求的handler，也就是controller
        mappedHandler = this.getHandler(processedRequest);
        if (mappedHandler == null) {
            this.noHandlerFound(processedRequest, response);
            return;
        }

        //获取HandlerAdapter，用来处理请求返回视图
        HandlerAdapter ha = this.getHandlerAdapter(mappedHandler.getHandler());
        String method = request.getMethod();
        mv = ha.handle(processedRequest, response, mappedHandler.getHandler());

        this.processDispatchResult(processedRequest, response, mappedHandler, mv);
    }


    private void processDispatchResult(HttpServletRequest request, HttpServletResponse response, HandlerExecutionChain mappedHandler, ModelAndView mv) {
        this.render(mv, request, response);
    }

    private void render(ModelAndView mv, HttpServletRequest request, HttpServletResponse response) {
        String viewName = mv.getViewName();
        View view=null;
        if (viewName != null) {
            view = this.resolveViewName(viewName, mv.getModelInternal(), request);
            if (view == null) {
                throw new RuntimeException("Could not resolve view with name '" + mv.getViewName() + "' in servlet with name '" + this.getServletName() + "'");
            }
        }
        view.render(mv.getModelInternal(), request, response);
    }

    private View resolveViewName(String viewName, Object modelInternal, HttpServletRequest request) {
        if (this.viewResolvers != null) {
            Iterator var5 = this.viewResolvers.iterator();

            while(var5.hasNext()) {
                ViewResolver viewResolver = (ViewResolver)var5.next();
                View view = viewResolver.resolveViewName(viewName);
                //前后缀补全
                if (view instanceof InternalResourceView){
                    String url = ((InternalResourceView) view).getUrl();
                    if (viewNameTranslator instanceof DefaultRequestToViewNameTranslator){
                        String prefix = ((DefaultRequestToViewNameTranslator) viewNameTranslator).getPrefix();
                        String suffix = ((DefaultRequestToViewNameTranslator) viewNameTranslator).getSuffix();
                        ((InternalResourceView) view).setUrl(prefix+url+suffix);
                    }

                }
                if (view != null) {
                    return view;
                }
            }
        }

        return null;
    }

    private HandlerAdapter getHandlerAdapter(Object handler) {
        if (this.handlerAdapters.get(0).support(handler)) {
            return this.handlerAdapters.get(0);
        }
        throw new RuntimeException("can not find handlerAdapters");
    }

    /**
     * 找不到处理链，也就是说找不到对应的Controller的方法，就404
     *
     * @param processedRequest
     * @param response
     */
    private void noHandlerFound(HttpServletRequest processedRequest, HttpServletResponse response) {
        try {
            response.sendError(404);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    /**
     * 获取执行该请求的处理链
     *
     * @param processedRequest
     * @return
     */
    private HandlerExecutionChain getHandler(HttpServletRequest processedRequest) {
        HandlerExecutionChain handlerExecutionChain = null;
        try {
            handlerExecutionChain = handlerMappings.get(0).getHandler(processedRequest);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return handlerExecutionChain;
    }

    /**
     * 判断请求是否带文件
     *
     * @param request
     * @return
     */
    private boolean checkMultipart(HttpServletRequest request) {
        String contentType = request.getContentType();
        if (contentType == null) {
            return false;
        }
        return request.getContentType().startsWith("multipart/");
    }

    /**
     * 初始化给映射，解析器等等
     */
    protected void initStrategies() {
        this.initHandlerMappings();
        this.initHandlerAdapters();
        this.initRequestToViewNameTranslator();
        this.initViewResolvers();
    }

    private void initViewResolvers() {
        viewResolvers=Collections.singletonList(new ContentNegotiatingViewResolver());
    }

    private void initRequestToViewNameTranslator() {
        viewNameTranslator=new DefaultRequestToViewNameTranslator();
    }

    /**
     * 源码中是搜索整个项目支持的controller类型（声明一个controller不仅仅可以用@Controller）
     * 不同的controller声明方式，会导致HandlerMapping的对应实现类不同
     * RequestMappingHandlerAdapter是@Controller的对应形式（简化书写，就放了一个）
     */
    private void initHandlerAdapters() {
        handlerAdapters = Collections.singletonList(new RequestMappingHandlerAdapter());
    }

    /**
     * 源码中是搜索整个项目支持的controller类型（声明一个controller不仅仅可以用@Controller）
     * 不同的controller声明方式，会导致HandlerMapping的对应实现类不同
     * RequestMappingHandlerMapping是@RequestMapping的对应形式（简化书写，就放了一个,并且扫描的任务也写在这里）
     */
    private void initHandlerMappings() {
        handlerMappings = Collections.singletonList(new RequestMappingHandlerMapping());
        AbstractHandlerMethodMapping.MappingRegistry mappingRegistry = abstractHandlerMethodMapping.getMappingRegistry();
        findController(mappingRegistry);
    }

    private void findController(AbstractHandlerMethodMapping.MappingRegistry mappingRegistry) {
        String path = this.getClass().getResource("/").getFile();
        File file = new File(path);
        treeFile(mappingRegistry, path, file);
    }

    private void treeFile(AbstractHandlerMethodMapping.MappingRegistry mappingRegistry, String path, File file) {
        for (File listFile : file.listFiles()) {
            if (listFile.isDirectory()) {
                if (listFile.listFiles().length > 0) {
                    treeFile(mappingRegistry, path, listFile);
                }
            } else {
                String filePath = listFile.toString();
                filePath = filePath.substring(path.length() - 1).replace("\\", ".");
                if (filePath.endsWith(".class")) {
                    filePath = filePath.substring(0, filePath.length() - 6);
                    try {
                        Class<?> aClass = this.getClass().getClassLoader().loadClass(filePath);
                        if (aClass.isAnnotationPresent(Controller.class)) {
                            for (Method method : aClass.getDeclaredMethods()) {
                                if (method.isAnnotationPresent(RequestMapping.class)) {
                                    MethodParameter[] methodParameters = new MethodParameter[method.getParameterTypes().length];
                                    for (int i = 0; i < method.getParameterTypes().length; i++) {
                                        Class<?> parameterType = method.getParameterTypes()[i];
                                        //因为反射是获取不到方法参数的参数名的，只能获取参数类型
                                        //获取参数名需要进行反编译，这里简略了，参数名都设置为""
                                        MethodParameter methodParameter = new MethodParameter(i, parameterType, "");
                                        methodParameters[i] = methodParameter;
                                    }
                                    HandlerMethod handlerMethod = new HandlerMethod(aClass, method, methodParameters);
                                    String value = ((RequestMapping) method.getAnnotation(RequestMapping.class)).value();
                                    mappingRegistry.getMappings().put(value, handlerMethod);
                                }
                            }
                        }
                    } catch (ClassNotFoundException e) {
                        e.printStackTrace();
                    }
                }
            }
        }
    }

}
